package org.zht.dormpro.dorm.aop;

import com.xiaoleilu.hutool.util.StrUtil;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.zht.dormpro.dorm.aop.annotation.ClearCache;
import org.zht.dormpro.dorm.aop.annotation.GetCache;
import org.zht.dormpro.dorm.cacher.service.IRedisTemplateService;

import java.lang.reflect.Method;
import java.util.List;

@Log4j
public class CacheAspect {
    @Autowired
    private IRedisTemplateService redisTemplateService;

    public Object putCache(ProceedingJoinPoint joinPoint) {
        Object result = null;
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法试
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        GetCache getCache = method.getAnnotation(GetCache.class);
        String fieldKey = getCache.fieldKey();
        //不是空,就是hash的格式处理
        if (StrUtil.isNotBlank(fieldKey)) {
            if (fieldKey.indexOf("#") == 0) {
                fieldKey = parseKey(fieldKey, method, joinPoint.getArgs());
                Class<?> returnType = method.getReturnType();
                Object o = redisTemplateService.hGet(getCache.key(), fieldKey, returnType);
                if (o != null) {
                    result = o;
                } else {
                    try {
                        result = joinPoint.proceed(args);
                        //将结果放到缓存
                        if(result!=null) {
                            redisTemplateService.hSet(getCache.key(), fieldKey, result);
                        }
                    } catch (Throwable throwable) {
                        log.info("执行方法:" + method.getName() + "出错!",throwable);
                    }
                }

            }
        } else if(getCache.type()!=Exception.class) {
            //否则字符串格式处理
            result = redisTemplateService.get(getCache.key(), getCache.type());
            if(result==null){
                try {
                    result = joinPoint.proceed(args);
                    //设置数据到缓存
                    if(result!=null) {
                        redisTemplateService.set(getCache.key(), result);
                    }
                } catch (Throwable throwable) {
                    log.info("执行方法:" + method.getName() + "出错!",throwable);
                }

            }

        } else {
            log.error("注解解析错误,执行方法:"+method.getName());
            try {
                result = joinPoint.proceed(args);
            } catch (Throwable throwable) {
                log.info("执行方法:" + method.getName() + "出错!",throwable);
            }
        }
        return result;
    }

    public void clearCache(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法试
        Method method = signature.getMethod();
        ClearCache getCache = method.getAnnotation(ClearCache.class);
        String[] fieldKeys = getCache.fieldKey();
        if (fieldKeys != null && fieldKeys.length > 0) {
            for (String fieldKey : fieldKeys) {
                if (fieldKey.indexOf("#") == 0) {
                    fieldKey = parseKey(fieldKey, method, joinPoint.getArgs());
                    try {
                        //移除缓存
                        redisTemplateService.hDel(getCache.key(), fieldKey);
                    } catch (Throwable throwable) {
                        log.info("执行方法:" + method.getName() + "出错!");
                    }
                }
            }
        }
    }

    private String parseKey(String key, Method method, Object[] args) {
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(key).getValue(context, String.class);
    }
}
